package com.gmrz.uaf.db.dao;

import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.protocol.v1.json.UAFSchemaBuilder;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.AuthenticationResponse;
import com.gmrz.uaf.protocol.v1.schema.FidoLog;
import com.gmrz.uaf.protocol.v1.schema.RegistrationResponse;
import com.gmrz.uaf.server.ServerContext;
import com.gmrz.uaf.servlet.v1.request.AuthSendContext;
import com.gmrz.uaf.servlet.v1.request.IDAuthContext;
import com.gmrz.uaf.servlet.v1.request.ReceiveReqPayload;
import com.gmrz.uaf.servlet.v1.request.RegSendContext;
import com.gmrz.util.Convert;
import com.gmrz.util.Strings;
import com.gmrz.util.UUIDGenerator;
import com.gmrz.util.db.DBUtil;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

import static com.gmrz.uaf.common.Constants.FIDO_LOG_EXTEND_TYPE_REQUEST;
import static com.gmrz.uaf.common.Constants.FIDO_LOG_EXTEND_TYPE_RESPONSE;

/**
 * Created by chentaikun on 16/12/26.
 */
public class BaseFidoLogDAO implements FidoLogDAO {
    private static final Logger LOG = LogManager.getLogger(BaseFidoLogDAO.class);
    private static final String _SQL_BATCH_INSERT="insert into t_fido_log(fl_id,transeq,tenant_id,auth_type,user_name,request,request_one,response,response_one," +
        "oper_type,oper_error,oper_code,local_ip,remote_ip,createts,trans_type,field1)" +
        "values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";

    private static final String LOG_EXTEND_SQL_BATCH_INSERT = "INSERT INTO t_fido_log_extend ( fido_log_extend_id,fl_id,extend_type,extend_order,extend_content,createts) VALUES(?,?,?,?,?,?)";

    private Connection conn_;

    @AssistedInject
    public BaseFidoLogDAO(@Assisted Connection conn_) {
        this.conn_ = conn_;
    }

    public BaseFidoLogDAO() {

    }
    public void insert(List<FidoLog> fidoLogs) throws DAOException {
        PreparedStatement s = null;
        PreparedStatement logExtends = null;
        // 判断是否由于数据长度过长，而将超长数据存入扩展表中
        boolean flag = false;
        try {
            s = this.conn_.prepareStatement(_SQL_BATCH_INSERT);
            logExtends = this.conn_.prepareStatement(LOG_EXTEND_SQL_BATCH_INSERT);
            for(FidoLog log:fidoLogs){
                int offset=1;
                String request = log.getRequest();
                String response = log.getResponse();
                try {
                    if(Strings.isNotBlank(log.getAuthType())) {
                        if ((log.getAuthType().equals(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) && log.getOperType() == Constants.Method.REG_FINISH.getOperType()) ||
                                (log.getAuthType().equals(Constants.AuthType.REMOTE_VOICEPRINT.getAuthType()) && log.getOperType() == Constants.Method.REG_FINISH.getOperType())) {
                            Gson gson = UAFSchemaBuilder.getGson();
                            Type reqType = new TypeToken<ReceiveReqPayload<RegSendContext>>() {
                            }.getType();
                            ReceiveReqPayload<RegSendContext> reqPayload = gson.fromJson(request, reqType);
                            String uafMessage = null;
                            if(reqPayload.getContext().isBase64()) {
                                uafMessage = new String(Convert.fromBase64(reqPayload.getUafResponse()));
                            }else {
                                uafMessage = reqPayload.getUafResponse();
                            }
                            Type type = new TypeToken<List<RegistrationResponse>>() {
                            }.getType();
                            List<RegistrationResponse> regResponses = gson.fromJson(uafMessage, type);
                            for (RegistrationResponse rr : regResponses) {
                                rr.getRegistration().setExtensions(null);
                            }
                            String uafMessageJson = gson.toJson(regResponses, type);
                            reqPayload.setUafResponse(uafMessageJson);
                            request = gson.toJson(reqPayload, reqType);
                        } else if ((log.getAuthType().equals(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) && log.getOperType() == Constants.Method.AUTH_FINISH.getOperType()) ||
                                (log.getAuthType().equals(Constants.AuthType.REMOTE_VOICEPRINT.getAuthType()) && log.getOperType() == Constants.Method.AUTH_FINISH.getOperType())) {
                            Gson gson = UAFSchemaBuilder.getGson();
                            Type reqType = new com.google.common.reflect.TypeToken<ReceiveReqPayload<AuthSendContext>>() {
                            }.getType();
                            ReceiveReqPayload<AuthSendContext> reqPayload = gson.fromJson(request, reqType);
                            String uafMessage = null;
                            if(reqPayload.getContext().isBase64()) {
                                uafMessage = new String(Convert.fromBase64(reqPayload.getUafResponse()));
                            }else {
                                uafMessage = reqPayload.getUafResponse();
                            }
                            Type type = new com.google.common.reflect.TypeToken<List<AuthenticationResponse>>() {
                            }.getType();
                            List<AuthenticationResponse> authResponses = gson.fromJson(uafMessage, type);
                            for (AuthenticationResponse ar : authResponses) {
                                ar.getAuthentication().setExtensions(null);
                            }
                            String uafMessageJson = gson.toJson(authResponses, type);
                            reqPayload.setUafResponse(uafMessageJson);
                            request = gson.toJson(reqPayload, reqType);
                        } else if ((log.getAuthType().equals(Constants.AuthType.REMOTE_IDAUTH.getAuthType()) && log.getOperType() == Constants.Method.IDAUTH_FINISH.getOperType()) ||
                                (log.getAuthType().equals(Constants.AuthType.REMOTE_IDFACEAUTH.getAuthType()) && log.getOperType() == Constants.Method.IDAUTH_FINISH.getOperType())) {
                            Gson gson = UAFSchemaBuilder.getGson();
                            Type reqType = new TypeToken<ReceiveReqPayload<IDAuthContext>>() {
                            }.getType();
                            ReceiveReqPayload<IDAuthContext> reqPayload = gson.fromJson(request, reqType);
                            String uafMessage = null;
                            if(reqPayload.getContext().isBase64()) {
                                uafMessage = new String(Convert.fromBase64(reqPayload.getUafResponse()));
                            }else {
                                uafMessage = reqPayload.getUafResponse();
                            }
                            Type type = new TypeToken<List<AuthenticationResponse>>() {
                            }.getType();
                            List<AuthenticationResponse> authResponses = gson.fromJson(uafMessage, type);
                            for (AuthenticationResponse ar : authResponses) {
                                ar.getAuthentication().setExtensions(null);
                            }
                            String uafMessageJson = gson.toJson(authResponses, type);
                            reqPayload.setUafResponse(uafMessageJson);
                            request = gson.toJson(reqPayload, reqType);
                        }
                    }
                }catch (Exception e){
                    LOG.error("split request error, message:"+e.getMessage()+", req:"+request);
                }

                String requestZero = null;
                String requestOne = null;
                String requestTwo = null;
                String responseZero = null;
                String responseOne = null;
                String responseTwo = null;

                String requestBase64 = Convert.toBase64(request.getBytes("UTF-8"));

                if(null != requestBase64 && requestBase64.length() >3800){
                    requestZero = requestBase64.substring(0,3800);
                    String r = requestBase64.substring(3800);
                    if(null != r && r.length() > 3800){
                        requestOne = r.substring(0,3800);
                        String r1 = r.substring(3800);
                        requestTwo = r1;
                    }else {
                        requestOne = r;
                    }

                }else {
                    requestZero = requestBase64;
                }

                String responseBase64 = Convert.toBase64(response.getBytes("UTF-8"));

                if(null != responseBase64 && responseBase64.length() >3800){
                    responseZero = responseBase64.substring(0,3800);
                    String r = responseBase64.substring(3800);
                    if(null != r && r.length() > 3800){
                        responseOne = r.substring(0,3800);
                        String r1 = r.substring(3800);
                        responseTwo = r1;
                    }else {
                        responseOne = r;
                    }

                }else {
                    responseZero = responseBase64;
                }



                String flID = UUIDGenerator.generateID();
                s.setString(offset++,flID);
                s.setString(offset++,log.getTransNo());
                s.setString(offset++,log.getAppID());
                s.setString(offset++,log.getAuthType());
                s.setString(offset++,log.getUserName());
                s.setString(offset++, requestZero);
                s.setString(offset++, requestOne);
                s.setString(offset++, responseZero);
                s.setString(offset++, responseOne);
                s.setInt(offset++,log.getOperType());
                s.setString(offset++,log.getOperError());
                s.setInt(offset++,log.getOperCode());
                s.setString(offset++,log.getLocalIP());
                s.setString(offset++,log.getRemoteIP());
                Calendar today = Calendar.getInstance();
                Timestamp currentTimestamp = new Timestamp(today.getTime().getTime());
                s.setTimestamp(offset++,currentTimestamp);
                s.setString(offset++,log.getTransType());
                String field1 = "1";
                s.setString(offset++,field1);
                s.addBatch();
                if(Strings.isNotBlank(requestTwo)){
                    int logExtendOrder = 1;
                    do{
                        String logExtendContent = null;
                        if(requestTwo.length() > 3800){
                            logExtendContent = requestTwo.substring(0,3800);
                            String tmp = requestTwo.substring(3800);
                            requestTwo = tmp;
                        }else {
                            logExtendContent = requestTwo;
                            requestTwo = null;
                        }
                        int index = 1;
                        logExtends.setString(index++,UUIDGenerator.generateID());
                        logExtends.setString(index++,flID);
                        logExtends.setInt(index++,FIDO_LOG_EXTEND_TYPE_REQUEST);
                        logExtends.setInt(index++,logExtendOrder++);
                        logExtends.setString(index++,logExtendContent);
                        logExtends.setTimestamp(index++,currentTimestamp);
                        logExtends.addBatch();
                        flag =true;
                    }while (Strings.isNotBlank(requestTwo));
                }

                if(Strings.isNotBlank(responseTwo)){
                    int logExtendOrder = 1;
                    do{
                        String logExtendContent = null;
                        if(responseTwo.length() > 3800){
                            logExtendContent = responseTwo.substring(0,3800);
                            String tmp = responseTwo.substring(3800);
                            responseTwo = tmp;
                        }else {
                            logExtendContent = responseTwo;
                            responseTwo = null;
                        }
                        int index = 1;
                        logExtends.setString(index++,UUIDGenerator.generateID());
                        logExtends.setString(index++,flID);
                        logExtends.setInt(index++,FIDO_LOG_EXTEND_TYPE_RESPONSE);
                        logExtends.setInt(index++,logExtendOrder++);
                        logExtends.setString(index++,logExtendContent);
                        logExtends.setTimestamp(index++,currentTimestamp);
                        logExtends.addBatch();
                        flag =true;
                    }while (Strings.isNotBlank(responseTwo));
                }
            }
            s.executeBatch();
            if(flag) {
                logExtends.executeBatch();
            }


        }catch (SQLException sqle){
            LOG.error(sqle);
            throw new DAOException(UAFErrorCode.DB_INSERT_FIDO_LOG_FAILED,sqle);
        } catch (UnsupportedEncodingException e) {
            LOG.error(e);
            throw new DAOException(UAFErrorCode.DB_INSERT_FIDO_LOG_FAILED,e);
        } finally {
            DBUtil.close(s);
            DBUtil.close(logExtends);
        }
    }
}
